﻿using PSE.Framework.Communication.Adapters.File;
using System.IO;
using PSE.Framework.Communication.Configuration.Adapters;
using System.Data;
using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Text;
using System.Xml.Xsl;
using PSE.Framework.Communication.Configuration.Exceptions;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Diagnostics;
using PSE.Framework.Communication.Configuration.File;
using PSE.Framework.Communication.Configuration.Adapters.File.XmlFile;
using PSE.Framework.Communication.Configuration.File.XmlFile;

namespace PSE.Framework.Communication.Adapters.File.XmlFile
{
    public class AsyncXmlFileInputAdapter : AsyncFileInputAdapter<XmlFileInputConfiguration>
    {
        private XPathNodeIterator _iterator;
        private string _transformPath;
        private Dictionary<string, XmlSerializer> _serializers;

        public AsyncXmlFileInputAdapter()
        {

        }

        public override void Open(XmlFileInputConfiguration config, object parameter)
        {
            base.Open(config, parameter);

            _serializers = new Dictionary<string, XmlSerializer>();

            // Criando serializers
            foreach (XmlFileInputAdapterActionConfiguration action in this.Configuration.Actions)
            {
                if (action.DeserializedMessageType != null)
                {
                    _serializers.Add(action.Name, new XmlSerializer(action.DeserializedMessageType));
                }
            }
        }

        protected override void OpenFile(string fullPath)
        {
            _transformPath = null;
            string file = fullPath;

            // Validando arquivo
            if (!string.IsNullOrEmpty(this.Configuration.ValidationFile))
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.Schemas.Add(null, this.Configuration.ValidationFile);
                settings.ValidationType = ValidationType.Schema;

                // Percorre todo o arquivo para validar
                using (XmlReader reader = XmlReader.Create(fullPath, settings))
                {
                    while (reader.Read()) { }
                }
            }


            // Transformando arquivo
            if (!string.IsNullOrEmpty(this.Configuration.TransformFile))
            {
                // Validando se a pasta de transformação foi informada
                if (string.IsNullOrEmpty(this.Configuration.TransformPath))
                    throw new InvalidAdapterConfigurationException("TransformPath must be informed with TransformFile", this.Configuration.Name);

                // Carregando Xslt
                XslCompiledTransform transform = new XslCompiledTransform();
                transform.Load(this.Configuration.TransformFile);

                // Montando path do arquivo transformado
                _transformPath = this.Configuration.TransformPath + @"\" + Path.GetFileNameWithoutExtension(fullPath) + "_Transformed" + Path.GetExtension(fullPath);

                try
                {
                    // Transformando arquivo
                    transform.Transform(fullPath, _transformPath);
                }
                catch
                {
                    // Se ocorrer um erro na transformação deve limpar _transformDest
                    // para que o arquivo transformado não seja movido
                    _transformPath = null;
                    throw;
                }

                // Arquivo a ser importado é o arquivo transformado
                file = _transformPath;
            }


            // Carregando documento
            XPathDocument document = new XPathDocument(file);

            // Criando um XPathNavigator
            XPathNavigator nav = document.CreateNavigator();

            // Criando um XPathExpression
            XPathExpression expr = nav.Compile(this.Configuration.XPath);

            // Criando um XPathNodeIterator
            _iterator = nav.Select(expr);
        }

        protected override object ReadRecord()
        {
            // Avança para o próximo registro
            if (_iterator.MoveNext())
            {
                // Se conseguiu avançar, retorna string com o node
                return _iterator.Current.OuterXml;
            }
            else
            {
                // Se não existem mais registros, retorna null
                return null;
            }
        }

        protected override object Deserialize(object message, string action)
        {
            object deserializedMessage = message;

            // Se não tiver um type configurado retorna a string xml
            if (_serializers.ContainsKey(action))
            {
                StringReader reader = new StringReader(System.Convert.ToString(message));

                deserializedMessage = _serializers[action].Deserialize(reader);
            }

            return deserializedMessage;
        }

        protected override void CloseFile()
        {
            _iterator = null;
            _serializers = null;
        }

        protected override int GetFileRowCount()
        {
            return this._iterator.Count;
        }

        protected override string MoveFile(string fullFilePath, string destinationDir)
        {
            // Movendo arquivo transformado
            if (_transformPath != null)
            {
                base.MoveFile(_transformPath, destinationDir);
                _transformPath = null;
            }

            // Movendo arquivo original
            return base.MoveFile(fullFilePath, destinationDir);
        }

        protected override void DeleteFile(string fullFilePath)
        {
            // Excluindo arquivo transformado
            if (_transformPath != null)
            {
                base.DeleteFile(_transformPath);
                _transformPath = null;
            }

            // Excluindo arquivo original
            base.DeleteFile(fullFilePath);
        }
    }

    //public class XPathReader : XPathNavigator
    //{
    //    public XmlTextReader Node;

    //    public XPathReader(Stream stream)
    //    {
    //        Node = new XmlTextReader(stream);
    //    }

    //    public XPathReader(TextReader reader)
    //    {
    //        Node = new XmlTextReader(reader);
    //    }

    //    public XPathReader(String url)
    //    {
    //        Node = new XmlTextReader(url);
    //    }

    //    public XPathReader(XPathReader a)
    //    {
    //        this.Node = a.Node;
    //    }

    //    public override XPathNavigator Clone()
    //    {
    //        return new XPathReader(this);
    //    }

    //    public override XPathNodeType NodeType
    //    {
    //        get
    //        {
    //            if (Node.ReadState == ReadState.Initial)
    //                return XPathNodeType.Root;

    //            switch (Node.NodeType)
    //            {
    //                case XmlNodeType.Document: return XPathNodeType.Root;
    //                case XmlNodeType.XmlDeclaration: Debug.Assert(false, "Should not be here"); throw new Exception("Invalid node type");
    //                case XmlNodeType.CDATA: return XPathNodeType.Text;
    //                case XmlNodeType.Element: return XPathNodeType.Element;
    //                case XmlNodeType.EndElement: Debug.Assert(false, "Should not be here"); throw new Exception("Invalid node type");
    //                case XmlNodeType.EntityReference: Debug.Assert(false, "Should not be here"); throw new Exception("Invalid node type");
    //                case XmlNodeType.Attribute: return XPathNodeType.Attribute;
    //                case XmlNodeType.Text: return XPathNodeType.Text;
    //                case XmlNodeType.Whitespace: return XPathNodeType.Whitespace;
    //                case XmlNodeType.SignificantWhitespace: return XPathNodeType.SignificantWhitespace;
    //                case XmlNodeType.Comment: return XPathNodeType.Comment;
    //                case XmlNodeType.ProcessingInstruction: return XPathNodeType.ProcessingInstruction;
    //                case XmlNodeType.None: return XPathNodeType.Text;
    //                // TODO add others
    //                default: Debug.Assert(false, Node.NodeType.ToString()); throw new Exception("Invalid node type");
    //            }
    //        }
    //    }

    //    public override string LocalName
    //    {
    //        get { return Node.LocalName; }
    //    }

    //    public override string Name
    //    {
    //        get { return Node.Name; }
    //    }

    //    public override string NamespaceURI
    //    {
    //        get { return Node.NamespaceURI; }
    //    }

    //    public override string Prefix
    //    {
    //        get { return Node.Prefix; }
    //    }

    //    public override string Value
    //    {
    //        get { return Node.Value; }
    //    }

    //    public override String BaseURI
    //    {
    //        get { return Node.BaseURI; }
    //    }

    //    public override String XmlLang
    //    {
    //        get { return Node.XmlLang; }
    //    }

    //    public override bool IsEmptyElement
    //    {
    //        get { return Node.IsEmptyElement; }
    //    }

    //    public override XmlNameTable NameTable
    //    {
    //        get { return Node.NameTable; }
    //    }

    //    public override bool HasAttributes
    //    {
    //        get { return Node.HasAttributes; }
    //    }

    //    public override string GetAttribute(string localName, string namespaceURI)
    //    {
    //        return (Node.GetAttribute(localName, namespaceURI));
    //    }

    //    private string GetAttribute(string name)
    //    {
    //        return (Node.GetAttribute(name));
    //    }

    //    public override bool MoveToAttribute(string localName, string namespaceURI)
    //    {
    //        return (Node.MoveToAttribute(localName, namespaceURI));
    //    }

    //    public override bool MoveToFirstAttribute()
    //    {
    //        return (Node.MoveToFirstAttribute());
    //    }

    //    public override bool MoveToNextAttribute()
    //    {
    //        return (Node.MoveToNextAttribute());
    //    }

    //    public override string GetNamespace(string localName)
    //    {
    //        return (GetAttribute(localName));
    //    }

    //    public override bool MoveToNamespace(string localName)
    //    {
    //        return MoveToAttribute(localName, @"http://www.w3.org/2000/xmlns/");
    //    }

    //    public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope)
    //    {
    //        while (Node.MoveToNextAttribute())
    //        {
    //            if (Node.LocalName == "xmlns")
    //                return true;
    //        }
    //        return false;
    //    }

    //    public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope)
    //    {
    //        while (Node.MoveToNextAttribute())
    //        {
    //            if (Node.LocalName == "xmlns")
    //                return true;
    //        }
    //        return false;
    //    }

    //    public override bool HasChildren
    //    {
    //        // TODO
    //        get
    //        {
    //            return false;
    //        }
    //    }

    //    public override bool MoveToNext()
    //    {
    //        if (!Node.EOF)
    //        {
    //            Node.Skip();
    //            return (Node.NodeType != XmlNodeType.EndElement && !Node.EOF);
    //        }
    //        return false;
    //    }

    //    public override bool MoveToPrevious()
    //    {
    //        throw (new Exception("Invalid XPath Expression for forward only stream"));

    //    }

    //    public override bool MoveToFirst()
    //    {
    //        throw (new Exception("Invalid XPath Expression for forward only stream"));
    //    }

    //    public override bool MoveToFirstChild()
    //    {
    //        if (Node.ReadState == ReadState.Initial)
    //        {
    //            Node.MoveToContent();
    //            Debug.Assert(Node.NodeType == XmlNodeType.Element);
    //            return true;
    //        }
    //        if (Node.NodeType == XmlNodeType.Element)
    //        {

    //            Node.Read();
    //            if (Node.NodeType != XmlNodeType.EndElement)
    //                return true;
    //        }

    //        return false;
    //    }

    //    public bool MoveToChild(int i)
    //    {
    //        throw (new Exception("Invalid XPath Expression for forward only stream"));
    //    }

    //    public override bool MoveToParent()
    //    {
    //        switch (Node.NodeType)
    //        {
    //            case XmlNodeType.Document: return false;
    //            case XmlNodeType.Element: return false;
    //            case XmlNodeType.Text: return false;
    //            case XmlNodeType.Attribute:
    //                Node.MoveToElement();
    //                return true;
    //            default:
    //                return false;

    //        }
    //    }

    //    public override void MoveToRoot()
    //    {
    //        Debug.Assert(Node != null);
    //        if (Node.ReadState == ReadState.Initial)
    //            return;
    //        else
    //            throw (new Exception("Reader in not in initial state for Root node"));
    //    }

    //    public override bool MoveTo(XPathNavigator other)
    //    {
    //        ((XPathReader)other).Node = this.Node;
    //        return true;
    //    }

    //    public override bool MoveToId(string id)
    //    {
    //        return false;
    //    }

    //    public override bool IsSamePosition(XPathNavigator other)
    //    {
    //        return true;
    //    }
    //}
}